/**
 * 
 */
package com.miolr.module.core.api;

import java.io.IOException;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.BeanProperty;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.TypeSerializer;
import org.codehaus.jackson.map.ser.impl.PropertySerializerMap;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;

/**
 * @author iday
 * 
 */
public class HibernateProxySerializer extends JsonSerializer<Object> {
	/**
	 * Property that has proxy value to handle
	 */
	protected final BeanProperty _property;

	/**
	 * For efficient serializer lookup, let's use this; most of the time,
	 * there's just one type and one serializer.
	 */
	protected PropertySerializerMap _dynamicSerializers;

	/*
	 * /**********************************************************************
	 * /* Life cycle
	 * /**********************************************************************
	 */

	public HibernateProxySerializer() {
		_dynamicSerializers = PropertySerializerMap.emptyMap();
		_property = null;
	}

	/*
	 * /**********************************************************************
	 * /* JsonSerializer impl
	 * /**********************************************************************
	 */

	@Override
	public void serialize(Object value, JsonGenerator jgen,
			SerializerProvider provider) throws IOException,
			JsonProcessingException {
		Object proxiedValue = findProxied((HibernateProxy)value);
		if (proxiedValue == null) {
			provider.defaultSerializeNull(jgen);
			return;
		}
		findSerializer(provider, proxiedValue).serialize(proxiedValue, jgen,
				provider);
	}

	public void serializeWithType(HibernateProxy value, JsonGenerator jgen,
			SerializerProvider provider, TypeSerializer typeSer)
			throws IOException, JsonProcessingException {
		Object proxiedValue = findProxied(value);
		if (proxiedValue == null) {
			provider.defaultSerializeNull(jgen);
			return;
		}
		/*
		 * This isn't exactly right, since type serializer really refers to
		 * proxy object, not value. And we really don't either know static type
		 * (necessary to know how to apply additional type info) or other
		 * things; so it's not going to work well. But... we'll do out best.
		 */
		findSerializer(provider, proxiedValue).serializeWithType(proxiedValue,
				jgen, provider, typeSer);
	}

	/*
	 * /**********************************************************************
	 * /* Helper methods
	 * /**********************************************************************
	 */

	protected JsonSerializer<Object> findSerializer(
			SerializerProvider provider, Object value) throws IOException,
			JsonProcessingException {
		Class<?> type = value.getClass();
		PropertySerializerMap.SerializerAndMapResult result = _dynamicSerializers
				.findAndAddSerializer(type, provider, _property);
		if (_dynamicSerializers != result.map) {
			_dynamicSerializers = result.map;
		}
		return result.serializer;
	}

	/**
	 * Helper method for finding value being proxied, if it is available or if
	 * it is to be forced to be loaded.
	 */
	protected Object findProxied(HibernateProxy proxy) {
		LazyInitializer init = proxy.getHibernateLazyInitializer();
		if (init.isUninitialized()) {
			return null;
		}
		return init.getImplementation();
	}

}
